//////////////////////////////////////////////
// String.h
//
//////////////////////////////////////////////

/// Defines / Macros -------------------------

#pragma once

/// Forward decl -----------------------------

namespace nkMemory
{
	class StringView ;
}

/// Includes ---------------------------------

// nkMemory
#include "../Dll/DllDefines.h"

#include "BufferCast.h"

// nkLog
#include <NilkinsLog/Data/String.h>
#include <NilkinsLog/Data/StringView.h>

// Standards
#include <cstring>
#include <string>
#include <string_view>

/// Class ------------------------------------

namespace nkMemory
{
	class DLL_MEMORY_EXPORT String final
	{
		public :

			// Constructor, destructor
			String () noexcept ;
			String (const char* data) noexcept ;
			String (unsigned long long size) noexcept ;
			String (const char* data, unsigned long long size) noexcept ;
			String (StringView view) noexcept ;
			String (const String& other) noexcept ;
			String (String&& other) noexcept ;
			~String () ;

			// Getters
			char* getData () const ;
			unsigned long long getSize () const ;
			bool empty () const ;
			char front () const ;
			char& front () ;
			char back () const ;
			char& back () ;
			const char* begin () const ;
			char* begin () ;
			const char* end () const ;
			char* end () ;

			// Management
			void clear () ;
			void resize (unsigned long long size) ;

			// Utils
			BufferCast<StringView> split (StringView separator) ;

			// Operators
			String& operator= (const char* data) noexcept ;
			String& operator= (StringView other) noexcept ;
			String& operator= (const String& other) noexcept ;
			String& operator= (String&& other) noexcept ;
			char operator[] (unsigned long long index) const ;
			char& operator[] (unsigned long long index) ;
			void operator+= (char value) ;
			void operator+= (StringView other) ;
			String operator+ (char c) const ;
			String operator+ (const char* other) const ;
			String operator+ (StringView other) const ;
			bool operator== (const char* other) const ;
			bool operator== (StringView other) const ;
			bool operator!= (const char* other) const ;
			bool operator!= (StringView other) const ;

		public :

			// Inlined constructors
			template<typename T = std::string>
			String (const std::string& str) noexcept
			:	String (str.data(), str.size())
			{
				// Nothing to do
			}

			template<typename T = std::string_view>
			String (const std::string_view& str) noexcept
			:	String (str.data(), str.size())
			{
				// Nothing to do
			}

		public :

			// Inlined operators
			template<typename T = std::string>
			operator std::string () const
			{
				return std::string(_data, _size) ;
			}

			template<typename T = std::string_view>
			operator std::string_view () const
			{
				return std::string_view(_data, _size) ;
			}

			operator nkLog::String () const
			{
				return nkLog::String(_data, _size) ;
			}

			operator nkLog::StringView () const
			{
				return nkLog::StringView(_data, _size) ;
			}

			template<typename T = std::string>
			String& operator= (const std::string& data)
			{
				// Copy memory over
				resize(data.size()) ;
				memcpy(_data, &data[0], _size) ;

				return *this ;
			}

			template<typename T = std::string_view>
			String& operator= (const std::string_view& data)
			{
				// Beware of overwriting by ourselves
				if (_data == data.data())
					return *this ;

				// Copy memory over
				resize(data.size()) ;
				memcpy(_data, &data[0], _size) ;

				return *this ;
			}

		private :

			// Attributes
			// Data info
			char* _data ;
			unsigned long long _size ;
	} ;

	// Globals
	// Operators
	inline bool operator== (const char* c, const nkMemory::String& str)
	{
		return str == c ;
	}

	inline nkMemory::String operator+ (char c, const nkMemory::String& str)
	{
		nkMemory::String result (str.getSize() + 1) ;

		result.getData()[0] = c ;
		memcpy(&result.getData()[1], str.getData(), str.getSize()) ;

		return result ;
	}

	inline nkMemory::String operator+ (const char* c, const nkMemory::String& str)
	{
		size_t otherLength = strlen(c) ;
		nkMemory::String result (str.getSize() + otherLength) ;

		memcpy(result.getData(), c, otherLength) ;
		memcpy(&result.getData()[otherLength], str.getData(), str.getSize()) ;

		return result ;
	}
}